In [ ]:
pip install pandas matplotlib seaborn
In [87]:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.gridspec import GridSpec

Очистка даних¶

В коді знизу я очищую таблицю з переписом 1778 року: прибираю службові рядки, перетворюю широку структуру в довгу, зберігаю інформацію про стать, статус у родині, вік і те, чи був хрещений. Результат - акуратна таблиця, з якою вже можна працювати в аналізі або візуалізаціях.

In [88]:
df_1778_raw = pd.read_csv("2.2_Дунаєць_1778.xlsx - База.csv", header=None, skiprows=3, dtype=str)

# задаємо назви колонок вручну, бо в csv їх немає
df_1778_raw.columns = [
    "sheet", "person_id", "household_id", "source_house_id",
    "man", "woman", "num_total", "num_source",
    "first_name", "middle_name", "last_name", "family_status",
    "category", "class", "social_status", "age",
    "confessed", "not_confessed", "too_young"
]

# залишаємо тільки тих, у кого заповнене поле чоловік або жінка
df_1778 = df_1778_raw[df_1778_raw["man"].notna() | df_1778_raw["woman"].notna()].copy()

# тягнемо значення household_id, source_house_id і category вниз (там вони зазвичай лише в першому рядку)
df_1778["household_id"] = df_1778["household_id"].ffill()
df_1778["source_house_id"] = df_1778["source_house_id"].ffill()
df_1778["category"] = df_1778["category"].ffill()

# конвертуємо таблицю з wide-формату у long: один рядок — одна людина
people = []
for _, row in df_1778.iterrows():
    for gender_col in ["man", "woman"]:
        if pd.notna(row[gender_col]):
            # переводимо мітку «х» у булеве значення
            confessed_bool = None
            if str(row["confessed"]).strip() == "х":
                confessed_bool = True
            elif str(row["not_confessed"]).strip() == "х" or str(row["too_young"]).strip() == "х":
                confessed_bool = False

            people.append({
                "household_id": row["household_id"],
                "gender": "чоловік" if gender_col == "man" else "жінка",
                "first_name": row["first_name"],
                "middle_name": row["middle_name"],
                "last_name": row["last_name"],
                "family_status": row["family_status"],
                "category": row["category"],
                "social_status": row["social_status"],
                "age": pd.to_numeric(row["age"], errors="coerce"), 
                "confessed_bool": confessed_bool
            })

df_1778_clean = pd.DataFrame(people)
df_1778_clean = df_1778_clean[df_1778_clean["first_name"] != "Імʼя"]

# заповнюємо прізвище вниз, бо воно часто лише в першій людині з родини
df_1778_clean["last_name"] = df_1778_clean["last_name"].ffill()
df_1778_clean = df_1778_clean.reset_index(drop=True)
df_1778_clean
Out[88]:
household_id gender first_name middle_name last_name family_status category social_status age confessed_bool
0 1 чоловік Федор Кондратьев Лукашевич господар nuclear духовные 44.0 True
1 1 жінка Варвара Симева Лукашевич жена nuclear духовные 35.0 True
2 1 жінка Анна NaN Лукашевич дочь nuclear духовные 17.0 True
3 1 чоловік Тимофей NaN Лукашевич сын nuclear духовные 15.0 True
4 1 чоловік Пантелеймон NaN Лукашевич сын nuclear духовные 14.0 True
... ... ... ... ... ... ... ... ... ... ...
1596 104 чоловік Леонтий Афанасиев Кислая двоюродный брат Александра nuclear бездворные 25.0 True
1597 104 жінка Мотрона Михайлова Кислая жена его nuclear бездворные 22.0 True
1598 105 чоловік Герасим Фомик Мерник господар nuclear бездворные 43.0 True
1599 105 жінка Агафия Максимова Мерник жена его nuclear бездворные 42.0 True
1600 105 чоловік Лукьян NaN Мерник NaN nuclear бездворные 14.0 True

1601 rows × 10 columns

Тепер структура таблиці охоплює: ID людини, ID родини, стать, ім’я, по батькові, прізвище, сімейний статус, категорію родини, соціальний статус, вік та інформацію про те, чи була людина хрещена. Я намагалася доповнити колонку з по батькові - логіка полягала в тому, щоб на основі імені чоловіка в родині (наприклад, господаря чи дячка) автоматично формувати по батькові для дітей.

Однак через велику варіативність і несистемність у записах сімейного статусу виникало надто багато виняткових випадків, які важко було б коректно обробити в межах цього дослідження. До того ж, повне заповнення по батькові не дало б суттєвої додаткової інформації для подальшого аналізу, тому я вирішила зосередитись на інших, більш стабільних змінних.


У коді нижче я очистила таблицю з переписом населення 1897 року та привела її до зручного для аналізу формату. Зокрема, було видалено технічні колонки, які не містили аналітично цінної інформації (такі як посилання, ID рядків тощо), розбито повне ім’я на окремі поля — ім’я, по батькові та прізвище, нормалізовано стать (позначки “m” та “f” замінено на “чоловік” і “жінка” відповідно), а також перетворено деякі текстові поля на числові або булеві значення для подальшого аналізу. Т

In [89]:
df_1897_raw = pd.read_csv("2.1_Дунаєць.xlsx - База людей.csv")
df_1897 = df_1897_raw.rename(columns={
    "Кто заполнял базу": "source",
    "ID строки в базе": "row_id",
    "List ID": "list_id",
    "Link": "link",
    "ID Домохозяйствао": "household_id",
    "ID жилец": "person_id",
    "ФИО": "full_name",
    "Пол": "gender",
    "Глава хозяйства и глава семьи": "head_status",
    "Возраст": "age",
    "Семейный статус": "family_status",
    "Сословие, состояние или звание": "social_class",
    "Здесь ли родился": "born_here",
    "Место рождения": "birth_place",
    "Здесь ли приписан": "registered_here",
    "Здесь ли обыкновенно проживает": "lives_here",
    "Отметка об отсуствии": "absence_note",
    "Вероисповедание": "religion",
    "Родной язык": "native_language",
    "Умеет ли читать": "can_read",
    "Обучение": "education",
    "Профессия главное": "main_profession",
    "Профессия вспомогательное": "secondary_profession",
    "Положение по воинской повинности": "military_status",
    "Примітки": "notes"
})

# прибираємо порожні рядки без імен — вони не несуть корисної інформації
df_1897 = df_1897[df_1897["full_name"].notna()]

# розбиваємо повне ім’я на прізвище, ім’я та по батькові
def split_full_name(name):
    parts = str(name).split()
    if len(parts) == 3:
        return pd.Series({"last_name": parts[0], "first_name": parts[1], "middle_name": parts[2]})
    elif len(parts) == 2:
        return pd.Series({"last_name": parts[0], "first_name": parts[1], "middle_name": None})
    else:
        return pd.Series({"last_name": None, "first_name": None, "middle_name": None})

df_1897 = df_1897.join(df_1897["full_name"].apply(split_full_name))

# видаляємо технічні колонки та колонку з повним ім’ям — тепер вони нам не потрібні
df_1897 = df_1897.drop(columns=["source", "row_id", "list_id", "link", "full_name", "person_id"])

# замінюємо "m"/"f" на "чоловік"/"жінка" для зручності подальшого аналізу
df_1897["gender"] = df_1897["gender"].map({"m": "чоловік", "f": "жінка"}).fillna(df_1897["gender"])

df_1897["age"] = pd.to_numeric(df_1897["age"], errors="coerce")
df_1897["household_id"] = pd.to_numeric(df_1897["household_id"], errors="coerce")
df_1897["can_read"] = df_1897["can_read"].map({1: True, 0: False, "1": True, "0": False})
df_1897 = df_1897.reset_index(drop=True)
df_1897
Out[89]:
household_id gender head_status age family_status social_class born_here birth_place registered_here lives_here ... native_language can_read education main_profession secondary_profession military_status notes last_name first_name middle_name
0 1.0 чоловік husband 32.0 married cossack 1.0 Dunaec 1 1 ... ukr True rural school farmer NaN Nizhnij chin zapasu NaN Krasovskij Pantelejmon Ivanovich
1 1.0 жінка wife 28.0 married cossack 0.0 Chernigovskaya gub., Glukhovskij pov., Kholopk... 1 1 ... ukr False NaN farmer with husband NaN NaN NaN Krasovskaya Stefanida Artemieva
2 1.0 жінка daughter 5.0 unmarried cossack 1.0 Dunaec 1 1 ... ukr False NaN with father NaN NaN NaN Krasovskaya Anna Pantelejmonova
3 1.0 чоловік son 1.0 unmarried cossack 1.0 Dunaec 1 1 ... ukrm False NaN with father NaN NaN NaN Krasovskij Stefan Pantelejmonov
4 1.0 чоловік father 70.0 vidov cossack 1.0 Dunaec 1 1 ... ukr False NaN with son NaN NaN Slep na oba hlaza 10 let nazad Krasovskij Ivan Kondratov
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1540 261.0 жінка wife 30.0 married peasant-owner 0.0 Chernigovskaya gub., Glukhovskij pov., m. Glukhov 0 / Chernigovskaya gub., Glukhovskij pov., s. ... 1 ... ukr False NaN with husband NaN NaN NaN Gricunova Motrona Nikolaeva
1541 261.0 жінка daughter 9.0 unmarried peasant-owner 0.0 Chernigovskaya gub., Glukhovskij pov., Tuligol... 0 / Chernigovskaya gub., Glukhovskij pov., s. ... 1 ... ukr False NaN with father NaN NaN NaN Gricunova Elena Sergeeva
1542 261.0 чоловік son 7.0 unmarried peasant-owner 0.0 Chernigovskaya gub., Glukhovskij pov., m. Glukhov 0 / Chernigovskaya gub., Glukhovskij pov., s. ... 1 ... ukr False NaN with father NaN NaN NaN Gricunov Nikolaj Sergeev
1543 261.0 чоловік son 5.0 unmarried peasant-owner 0.0 Chernigovskaya gub., Glukhovskij pov., s. Godu... 0 / Chernigovskaya gub., Glukhovskij pov., s. ... 1 ... ukr False NaN with father NaN NaN NaN Gricunov Aleksandr Sergeev
1544 261.0 чоловік son 3.0 unmarried peasant-owner 0.0 Chernigovskaya gub., Glukhovskij pov., Tuligol... 0 / Chernigovskaya gub., Glukhovskij pov., s. ... 1 ... ukr False NaN with father NaN NaN NaN Gricunov Mikhail Sergeev

1545 rows × 22 columns

Агрегація даних та візуалізація¶

У цій частині я проаналізувала вікову структуру населення на основі переписів. Зокрема, було побудовано дашборд із кількох панелей, що включає:

  • Порівняльне вікове розподілення у вигляді накладених гістограм для обох років.
  • Середній вік населення за статтю у вигляді стовпчикової діаграми.
  • Статево-віковий розподіл у форматі 100% stacked area chart.
  • Дві демографічні піраміди для 1778 та 1897 років, які дозволяють побачити гендерний баланс у різних вікових групах.
In [92]:
sns.set(style="darkgrid")
main_color = "#4C72B0"
alt_color = "#DD8452"

# чистка даних для графіків
df_1778_clean = df_1778_clean[df_1778_clean["age"].notna()]
df_1897 = df_1897[df_1897["age"].notna()]

# визначення вікових груп
bins = list(range(0, 101, 10))
labels = [f"{i}-{i+9}" for i in bins[:-1]]
df_1778_clean["age_group"] = pd.cut(df_1778_clean["age"], bins=bins, labels=labels, right=False)
df_1897["age_group"] = pd.cut(df_1897["age"], bins=bins, labels=labels, right=False)

fig = plt.figure(figsize=(20, 16))
gs = GridSpec(3, 3, figure=fig)
fig.suptitle("Вікова структура населення: порівняння 1778 vs 1897", fontsize=18)

# 1. гістограма
ax1 = fig.add_subplot(gs[0, 0:2])
sns.histplot(df_1778_clean["age"], bins=range(0, 101, 5), ax=ax1, color=main_color, label='1778', alpha=0.6)
sns.histplot(df_1897["age"], bins=range(0, 101, 5), ax=ax1, color=alt_color, label='1897', alpha=0.6)
ax1.set_title("Порівняльне вікове розподілення")
ax1.set_xlabel("Вік")
ax1.set_ylabel("Кількість людей")
ax1.legend()

# 2. середній вік
ax2 = fig.add_subplot(gs[0, 2])
avg_1778 = df_1778_clean.groupby("gender")["age"].mean()
avg_1897 = df_1897.groupby("gender")["age"].mean()
avg_df = pd.DataFrame({"1778": avg_1778, "1897": avg_1897}).T
bars = avg_df.plot(kind='bar', ax=ax2, color=[main_color, alt_color])
ax2.set_title("Середній вік за статтю")
ax2.set_ylabel("Середній вік")
ax2.legend(title="Стать")
for p in bars.patches:
    value = round(p.get_height(), 1)
    ax2.annotate(f'{value}', (p.get_x() + p.get_width() / 2, p.get_height()),
                 ha='center', va='bottom', fontsize=10)

# 3. статево-віковий розподіл 1778
ax3 = fig.add_subplot(gs[1, 0])
gender_age_1778 = df_1778_clean.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
gender_age_1778_pct = gender_age_1778.div(gender_age_1778.sum(axis=1), axis=0)
gender_age_1778_pct = gender_age_1778_pct[["чоловік", "жінка"]]
ax3.stackplot(gender_age_1778_pct.index.astype(str),
              gender_age_1778_pct["чоловік"],
              gender_age_1778_pct["жінка"],
              labels=["чоловік", "жінка"],
              colors=[main_color, alt_color])
ax3.set_title("Статево-віковий розподіл (1778)")
ax3.set_ylabel("Частка")
ax3.legend()
ax3.tick_params(axis='x', rotation=45)

# 4. статево-віковий розподіл 1897
ax4 = fig.add_subplot(gs[2, 0])
gender_age_1897 = df_1897.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
gender_age_1897_pct = gender_age_1897.div(gender_age_1897.sum(axis=1), axis=0)
gender_age_1897_pct = gender_age_1897_pct[["чоловік", "жінка"]]
ax4.stackplot(gender_age_1897_pct.index.astype(str),
              gender_age_1897_pct["чоловік"],
              gender_age_1897_pct["жінка"],
              labels=["чоловік", "жінка"],
              colors=[main_color, alt_color])
ax4.set_title("Статево-віковий розподіл (1897)")
ax4.set_ylabel("Частка")
ax4.legend()
ax4.tick_params(axis='x', rotation=45)

# 5. піраміда 1778 
ax5 = fig.add_subplot(gs[1:, 1])
pyramid_1778 = df_1778_clean.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
pyramid_1778["чоловік"] *= -1
ax5.barh(pyramid_1778.index.astype(str), pyramid_1778["чоловік"], color=main_color, label="чоловік")
ax5.barh(pyramid_1778.index.astype(str), pyramid_1778["жінка"], color=alt_color, label="жінка")
ax5.set_title("Піраміда населення 1778")
ax5.set_xlabel("Кількість")
ax5.set_ylabel("Вікова група")
ax5.legend()

# 6. піраміда 1897 
ax6 = fig.add_subplot(gs[1:, 2])
pyramid_1897 = df_1897.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
pyramid_1897["чоловік"] *= -1
ax6.barh(pyramid_1897.index.astype(str), pyramid_1897["чоловік"], color=main_color, label="чоловік")
ax6.barh(pyramid_1897.index.astype(str), pyramid_1897["жінка"], color=alt_color, label="жінка")
ax6.set_title("Піраміда населення 1897")
ax6.set_xlabel("Кількість")
ax6.set_ylabel("Вікова група")
ax6.legend()

plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
No description has been provided for this image

Ключові інсайти зі зрівняльного аналізу по віку:¶

  • Зростання середнього віку за статтю У 1778 році середній вік був приблизно 23.5 років як у чоловіків, так і у жінок. У 1897 році ситуація значно покращилась: середній вік виріс до 25.2 у чоловіків і 27.7 у жінок. Це свідчить про покращення загальних умов життя та зменшення смертності.

  • Молоде населення домінує у 1778 році За гістограмою вікового розподілу видно, що у 1778 році значна частина населення була у віковій категорії 0-20 років. Загалом у цьому році спостерігається вища концентрація молоді порівняно з 1897.

  • Зростання кількості людей старшого віку у 1897 році У 1897 році більше людей доживають до похилого віку, зокрема у групах 60+, 70+ років. У 1778 таких майже немає, що видно як з гістограми, так і з пірамід.

  • Демографічна піраміда 1897 року більш збалансована Піраміди показують, що у 1897 році розподіл населення за віком є рівномірнішим і демонструє більш «стабільне» суспільство. У 1778 піраміда різко звужується з віком.


У цій частині дослідження я проаналізувала склад родин. Було побудовано дашборд, що включає:

  • Гістограму розподілу розмірів родин у двох роках - показує, які родини були найпоширенішими.
  • Стовпчикову діаграму середнього розміру родини для кожного року.
  • Boxplot-діаграми кількості чоловіків та жінок у родинах.
In [108]:
sns.set(style="darkgrid")
main_color = "#4C72B0"
alt_color = "#DD8452"

df_1778_clean['household_id'] = df_1778_clean['household_id'].astype(int)
df_1897['household_id'] = df_1897['household_id'].astype(int)

# групування по родинам
families_1778 = df_1778_clean.groupby("household_id").agg(
    family_size=("gender", "count"),
    num_men=("gender", lambda x: (x == "чоловік").sum()),
    num_women=("gender", lambda x: (x == "жінка").sum())
).reset_index()
families_1778["year"] = 1778
families_1897 = df_1897.groupby("household_id").agg(
    family_size=("gender", "count"),
    num_men=("gender", lambda x: (x == "чоловік").sum()),
    num_women=("gender", lambda x: (x == "жінка").sum())
).reset_index()
families_1897["year"] = 1897
families_all = pd.concat([families_1778, families_1897], ignore_index=True)

fig = plt.figure(figsize=(18, 10))
gs = GridSpec(2, 2, figure=fig)
fig.suptitle("Аналіз родин у 1778 і 1897 роках", fontsize=18)

# 1. розподіл розмірів родин
ax1 = fig.add_subplot(gs[0, 0])
bins_range = range(1, families_all["family_size"].max() + 2)
sns.histplot(
    data=families_all,
    x="family_size",
    hue="year",
    multiple="dodge",
    bins=bins_range,
    palette=[main_color, alt_color],
    ax=ax1
)
ax1.set_title("Розподіл розмірів родин")
ax1.set_xlabel("Кількість людей у родині")
ax1.set_ylabel("Кількість родин")

# 2. середній розмір родини
ax2 = fig.add_subplot(gs[0, 1])
avg_sizes = families_all.groupby("year")["family_size"].mean().reset_index()
sns.barplot(
    data=avg_sizes,
    x="year",
    y="family_size",
    hue="year", 
    palette=[main_color, alt_color],
    ax=ax2,
    legend=False
)
ax2.set_title("Середній розмір родини")
ax2.set_xlabel("Рік")
ax2.set_ylabel("Середній розмір")

# 3. кількість чоловіків
ax3 = fig.add_subplot(gs[1, 0])
sns.boxplot(
    data=families_all,
    x="year",
    y="num_men",
    hue="year", 
    palette=[main_color, alt_color],
    ax=ax3,
    legend=False
)
ax3.set_title("Кількість чоловіків у родинах")
ax3.set_xlabel("Рік")
ax3.set_ylabel("Осіб")

# 4. кількість жінок
ax4 = fig.add_subplot(gs[1, 1])
sns.boxplot(
    data=families_all,
    x="year",
    y="num_women",
    hue="year", 
    palette=[main_color, alt_color],
    ax=ax4,
    legend=False
)
ax4.set_title("Кількість жінок у родинах")
ax4.set_xlabel("Рік")
ax4.set_ylabel("Осіб")

plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
No description has been provided for this image

Основні інсайти з графіків¶

  • Розмір родин значно зменшився у 1897 році. Якщо в 1778 році зустрічаються родини з чисельністю понад 30 осіб (і навіть вище 70!), то в 1897 році більшість родин обмежується 5–10 людьми.

  • Середній розмір родини зменшився вдвічі — із приблизно 15 осіб у 1778 до менш як 7 у 1897. Це може свідчити про перехід від розширених до нуклеарних родин.

  • Розподіл за статтю всередині родин також став компактнішим. Boxplot показує, що у 1778 році було значно більше чоловіків та жінок у родині (медіани — 6–8 осіб), тоді як у 1897 — 3–4.

  • У 1778 році були надзвичайно великі господарства, що видно по численним "вусам" та викидам на boxplot'ах. У 1897 таких випадків значно менше.

  • Зменшення кількості великих родин може бути пов’язане як із демографічними факторами (війни, хвороби), так і з соціальними змінами (поділ земель, нові економічні моделі ведення господарства).


У цій частині дослідження я проаналізувала шлюбний стан населення за переписом 1897 року.

На жаль, дані за 1778 рік є надто фрагментарними та архаїчними, тож побудувати повноцінну візуалізацію для цього періоду не вдалося. Уся представлена аналітика базується на даних за 1897 рік.

Що зображено на дашборді?

  • Карта теплових значень: кількість людей за віком та шлюбним статусом Показує, у якому віці найчастіше перебувають у статусі "одружений", "неодружений" або "вдівець/вдова".

  • Кругова діаграма: розподіл шлюбних статусів усього населення Відображає загальні частки married / unmarried / widowed у структурі населення.

  • Boxplot: розподіл віку за шлюбним статусом Дає змогу побачити, у якому віці найчастіше вступають у шлюб, залишаються неодруженими або стають вдівцями.

  • Карта теплових часток: частка кожного шлюбного статусу в кожній віковій групі Дозволяє виявити, які вікові групи найчастіше відповідають тому чи іншому статусу.

In [119]:
sns.set(style="darkgrid")
mako_palette = sns.color_palette("mako", n_colors=3) 
mako_cmap = sns.color_palette("mako", as_cmap=True)  

heatmap_data = df_1897.groupby(['age', 'family_status']).size().unstack(fill_value=0)
normalized = heatmap_data.div(heatmap_data.sum(axis=1), axis=0)
pie_data = df_1897['family_status'].value_counts()

fig = plt.figure(figsize=(20, 12))
gs = GridSpec(2, 2, figure=fig)
fig.suptitle("Аналіз шлюбного статусу населення у 1897 році", fontsize=20)

# абсолютна кількість хітмап
ax1 = fig.add_subplot(gs[0, 0])
sns.heatmap(heatmap_data, cmap=mako_cmap, linewidths=0.3, ax=ax1)
ax1.set_title("Кількість людей за віком та шлюбним статусом")
ax1.set_xlabel("Шлюбний статус")
ax1.set_ylabel("Вік")

# розподіл статусів
ax2 = fig.add_subplot(gs[0, 1])
ax2.pie(pie_data, labels=pie_data.index, autopct='%1.1f%%', startangle=140, colors=mako_palette)
ax2.set_title("Розподіл шлюбного статусу")

# вік за статусом
ax3 = fig.add_subplot(gs[1, 0])
sns.boxplot(data=df_1897, x='family_status', y='age', hue='family_status', palette=mako_palette, ax=ax3, legend=False)
ax3.set_title("Розподіл віку за шлюбним статусом")
ax3.set_xlabel("Шлюбний статус")
ax3.set_ylabel("Вік")
ax3.tick_params(axis='x', rotation=15)

# частка статусів у кожному віці ще раз хітмап
ax4 = fig.add_subplot(gs[1, 1])
sns.heatmap(normalized, cmap=mako_cmap, linewidths=0.3, ax=ax4)
ax4.set_title("Частка шлюбних статусів у кожній віковій групі")
ax4.set_xlabel("Шлюбний статус")
ax4.set_ylabel("Вік")

plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
No description has been provided for this image

Інсайти:¶

  1. Найбільша частка населення — неодружені Згідно з круговою діаграмою:

    • 51.1% населення були неодруженими
    • 42.0% - у шлюбі
    • лише 6.8% - вдівці/вдови Це може свідчити про загальну молодість населення або високий поріг вступу в шлюб.
  2. Шлюб найпоширеніший у віковому діапазоні 25–50 років За абсолютною heatmap видно, що найбільше людей у статусі married припадає на середній вік - приблизно від 25 до 50 років. Саме цей вік був найактивнішим щодо укладення шлюбів.

  3. Вдівство переважає у старших вікових групах (60+) Відповідно до heatmap та boxplot, статус vidov (вдова/вдівець) різко зростає після 60 років. Це свідчить про високу смертність одного з подружжя в старшому віці.

  4. Неодружені - переважно молодь до 30 років Розподіл віку показує, що unmarried мають найнижчий медіанний вік. Це логічно — молодь ще не вступила в шлюб або перебуває в «активному шлюбному віці».